package johnkkk.com.system_k;

import android.opengl.GLES32;
import android.os.Build;
import android.support.annotation.RequiresApi;

import com.graphics.GLGraphics;
import com.math3d.Vector2f;
import com.math3d.Vector2fv;
import com.math3d.Vector3f;
import com.system.GLLog;
import com.tools.GLBufferTools;
import com.tools.SizeOf;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.microedition.khronos.opengles.GL;

/**
 * Created by JhonKkk on 2018/11/3.
 * 用于管理地形的四叉树
 */

public class QuadTreeAtTerrain {
    //最小节点每个区间网格数目(mStrength*mStrength,默认为4*4)
    private int mStrength = 4;
    private QuadTree mRoot;
    //渲染列表
    private List<QuadTree> mRenderList;
    public static class QuadTree{
        public static class TerrainTriangle{
            private Vector3f p0, p1, p2;
            private Vector3f normal;
            private Vector3f line1;
            private Vector3f line2;
            //用于判断点是否在三角形的辅助向量
            private Vector3f yDir;
            private Vector3f temp = new Vector3f();

            public TerrainTriangle(Vector3f p0, Vector3f p1, Vector3f p2) {
                this.p0 = p0;
                this.p1 = p1;
                this.p2 = p2;
                scaleNormal();
            }

            @Override
            public String toString() {
                return "TerrainTriangle{" +
                        "p0=" + p0 +
                        ", p1=" + p1 +
                        ", p2=" + p2 +
                        ", normal=" + normal +
                        ", yDir=" + yDir +
                        '}';
            }

            /**
             * 在x-z平面判断点是否在三角形中
             * @param x
             * @param z
             * @return
             */
            public final boolean contains(float x, float z){
                temp.x = x;
                temp.z = z;
                temp = temp.sub(p1);
                temp.y = 0;
                temp = temp.cross(line2);
                temp.normalize();
                //GLLog.loge("当前三角形[" + p0 + "," + p1 + "," + p2 + "],yDir:" + yDir + ",点乘向量:" + temp + ",结果" + (temp.dot(yDir) > 0));
                return temp.dot(yDir) > 0;
            }
            public final boolean contains2(float x, float z){
                Vector3f p = new Vector3f(x, 0, z);
                Vector3f pa = p.sub(p0);
                Vector3f pb = p.sub(p1);
                Vector3f pc = p.sub(p2);
                /*
                t1 = PA^PB,

                t2 = PB^PC,

                t3 = PC^PA,
                * */
                float t1 = pa.cross(pb).dot(yDir);
                float t2 = pb.cross(pc).dot(yDir);
                float t3 = pc.cross(pa).dot(yDir);
                return (t1 > 0 && t2 > 0 && t3 > 0) || (t1 < 0 && t2 < 0 && t3 < 0);
            }
            public final void scaleNormal(){
                //计算法线
                line1 = p0.sub(p1);
                line2 = p2.sub(p1);
                normal = line1.cross(line2);
                normal.normalize();
                Vector3f v1 = new Vector3f(p0.x, 0, p0.z);
                Vector3f v2 = new Vector3f(p1.x, 0, p1.z);
                Vector3f v3 = new Vector3f(p2.x, 0, p2.z);
                line1 = v1.sub(v2);
                line2 = v3.sub(v2);
                yDir = line1.cross(line2);
                yDir.normalize();
            }
        }
        //p3-------------p2
        //|
        //|
        //p0------------p1
        public Vector2f p0, p1, p2, p3;
        public Vector2f n0, n1, n2, n3;
        public int mDepth;
        public QuadTree mParent;
        public List<QuadTree> mChild;
        public int mVAO;
        public int mLineVAO;
        public int mIndexCount;
        public int mLineIndexCount;
        public List<TerrainTriangle> mTerrainTriangles;

        public QuadTree(Vector2f p0, Vector2f p1, Vector2f p2, Vector2f p3) {
            this.p0 = new Vector2f(p0);
            this.p1 = new Vector2f(p1);
            this.p2 = new Vector2f(p2);
            this.p3 = new Vector2f(p3);
            n0 = markDVec(p0, 5);
            n1 = markDVec(p1, 5);
            n2 = markDVec(p2, 5);
            n3 = markDVec(p3, 5);
        }

        private Vector2f markDVec(Vector2f p0, float rectLength) {
            Vector2f v = new Vector2f(p0.x * rectLength, p0.y * rectLength);
            return v;
        }
        private Vector2fv markDVec(float x, float z){
            Vector2fv v = new Vector2fv(x, z);
            v.normalize();
            return v;
        }

        /**
         * 判断线段是否和矩形相交<br/>
         * 假设x > x2
         * z > z2
         * @param x
         * @param z
         * @param x2
         * @param z2
         * @return
         */
        public final boolean checkInRect(float x, float z, float x2, float z2){
            //p0-p1
            if(p1.x >= x2 && x >= p0.x && p1.y >= z2 && z >= p1.y)return true;
            //p1-p2
            if(p1.x >= x2 && x >= p2.x && p1.y >= z2 && z >= p2.y)return true;
            //p2-p3
            if(p2.x >= x2 && x >= p3.x && p2.y >= z2 && z >= p3.y)return true;
            //p3-p0
            if(p0.x >= x2 && x >= p3.x && p0.y >= z2 && z >= p3.y)return true;
            return false;
        }
        public final boolean checkTriangles(float line1[], float line2[], float line3[]){
            return contains(line1[2], line1[3]) || checkInRect(line1[0], line1[1], line1[2], line1[3])
                    || checkInRect(line2[0], line2[1], line2[2], line2[3])
                    || checkInRect(line3[0], line3[1], line3[2], line3[3]);
        }
        public final boolean contains(float x, float z){
            return (x >= p3.y * 5 && x <= p0.y * 5 && z >= p0.x * 5 && z <= p1.x * 5);
        }
        private final double scaleDistance(Vector2f p, float x, float z){
            return Math.abs(p.y - x) + Math.abs(p.x - z);
        }
        public final boolean checkViewRect(float x, float z, Vector2fv at, double viewDot, float far){
            //1.检查eye点是否在当前节点管辖区域中
            //2.检查管辖区域4个顶点与朝向向量点乘,结果值应该小于等于视锥体一半
            //3.检查管辖区域4个顶点与eye点的距离是否超过指定长度
            if(contains(x, z))return true;
            //if(at.dot(markDVec(n0.y - x, n0.x - z)) < viewDot && at.dot(markDVec(n1.y - x, n1.x - z)) < viewDot && at.dot(markDVec(n2.y - x, n0.x - z)) < viewDot && at.dot(markDVec(n3.y - x, n3.x - z)) < viewDot)return false;
            if(scaleDistance(n0, x, z) <= far || scaleDistance(n1, x, z) <= far || scaleDistance(n2, x, z) <= far || scaleDistance(n3, x, z) <= far)return true;
            return false;
        }
        public final boolean check(float x, float z, float l){
            //用圆形包围体粗略检查该地形区域是否在视锥体中
            float s0 = Math.abs((p0.x - x)) + Math.abs((p0.y - z));
            float s1 = Math.abs((p1.x - x)) + Math.abs((p1.y - z));
            float s2 = Math.abs((p2.x - x)) + Math.abs((p2.y - z));
            float s3 = Math.abs((p3.x - x)) + Math.abs((p3.y - z));
            return s0 <= l || s1 <= l || s2 <= l || s3 <= l;
        }

        @Override
        public String toString() {
            return "QuadTree{" +
                    "p0=" + p0 +
                    ", p1=" + p1 +
                    ", p2=" + p2 +
                    ", p3=" + p3 +
                    '}';
        }
    }
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public final void markQuadTree(Terrain terrain){
        if(terrain == null){
            return;
        }
        GLLog.loge("w*h:" + terrain.getmTerrainWidth() + "," + terrain.getmTerrainHeight());
        //计算树深度
        int count = (terrain.getmTerrainWidth()) * (terrain.getmTerrainHeight()) / (mStrength * mStrength);
        int depth = 0;
        do{
            depth += 1;
            count /= 4;
        }while(count != 1);
        GLLog.loge("深度:" + (depth + 1));
        mRoot = new QuadTree(new Vector2f(0, terrain.getmTerrainHeight()), new Vector2f(terrain.getmTerrainWidth(), terrain.getmTerrainHeight()), new Vector2f(terrain.getmTerrainWidth(), 0), new Vector2f(0, 0));
        mRoot.mDepth = depth;
        buildQuadTree(mRoot, mRoot.mDepth - 1, terrain);
        GLLog.loge("构建树成功!");
        mRenderList = new ArrayList<>();
    }
    private boolean is = false;
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public final void buildQuadTree(QuadTree parent, int depth, Terrain terrain){
        if(depth > 0){
            //将当前节点细分为4个区域
            int x = (int) ((int) ((parent.p1.x - parent.p0.x) / 2) + parent.p3.x);
            int y = (int) ((int) ((parent.p0.y - parent.p3.y) / 2) + parent.p3.y);
            Vector2f centerPoint = new Vector2f(x, y);
            QuadTree child0 = new QuadTree(new Vector2f(parent.p0), new Vector2f(x, parent.p0.y), centerPoint, new Vector2f(parent.p0.x, y));
            child0.mDepth = depth;
            QuadTree child1 = new QuadTree(new Vector2f(x, parent.p0.y), new Vector2f(parent.p1), new Vector2f(parent.p1.x, y), new Vector2f(centerPoint));
            child1.mDepth = depth;
            QuadTree child2 = new QuadTree(new Vector2f(centerPoint), new Vector2f(parent.p1.x, y), new Vector2f(parent.p2), new Vector2f(x, parent.p2.y));
            child2.mDepth = depth;
            QuadTree child3 = new QuadTree(new Vector2f(parent.p0.x, y), new Vector2f(centerPoint), new Vector2f(x, parent.p3.y), new Vector2f(parent.p3));
            child3.mDepth = depth;
            parent.mChild = new ArrayList<>();
            parent.mChild.add(child0);
            parent.mChild.add(child1);
            parent.mChild.add(child2);
            parent.mChild.add(child3);
            for(int i = 0;i < parent.mChild.size();i++){
//                GLLog.loge("parent.mChild.get(i).mDepth - 1 = " + (parent.mChild.get(i).mDepth - 1));
                buildQuadTree(parent.mChild.get(i), parent.mChild.get(i).mDepth - 1, terrain);
            }
        }
        else{
            //将当前节点细分为4个区域
            int x = (int) ((int) ((parent.p1.x - parent.p0.x) / 2) + parent.p3.x);
            int y = (int) ((int) ((parent.p0.y - parent.p3.y) / 2) + parent.p3.y);
            Vector2f centerPoint = new Vector2f(x, y);
            QuadTree child0 = new QuadTree(new Vector2f(parent.p0), new Vector2f(x, parent.p0.y), centerPoint, new Vector2f(parent.p0.x, y));
            child0.mDepth = depth;
            QuadTree child1 = new QuadTree(new Vector2f(x, parent.p0.y), new Vector2f(parent.p1), new Vector2f(parent.p1.x, y), new Vector2f(centerPoint));
            child1.mDepth = depth;
            QuadTree child2 = new QuadTree(new Vector2f(centerPoint), new Vector2f(parent.p1.x, y), new Vector2f(parent.p2), new Vector2f(x, parent.p2.y));
            child2.mDepth = depth;
            QuadTree child3 = new QuadTree(new Vector2f(parent.p0.x, y), new Vector2f(centerPoint), new Vector2f(x, parent.p3.y), new Vector2f(parent.p3));
            child3.mDepth = depth;
            parent.mChild = new ArrayList<>();
            parent.mChild.add(child0);
            parent.mChild.add(child1);
            parent.mChild.add(child2);
            parent.mChild.add(child3);
            markVAO(child0, terrain);
            markVAO(child1, terrain);
            markVAO(child2, terrain);
            markVAO(child3, terrain);
        }
    }
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    private final void markVAO(QuadTree parent, Terrain terrain){
        //构建最底层节点,用于渲染
        //计算顶点偏移量
//        GLLog.loge("当前区域:" + parent);
        int offset = (int) ((terrain.getmTerrainWidth() + 1));
        int startOffset = (int) (parent.p3.x / (terrain.getmTerrainWidth()) + parent.p3.x % (terrain.getmTerrainWidth()));
        startOffset = (int) ((int) (parent.p3.y * (terrain.getmTerrainWidth() + 1)) + parent.p3.x);
        int startEnd = (int) (startOffset + (parent.p2.x - parent.p3.x)) + 1;
//        GLLog.loge("当前offset:" + offset + ";startOffset:" + startOffset + ",startEnd:" + startEnd);
        int zCount = (int) (parent.p0.y - parent.p3.y);
//        GLLog.loge("zCount -- " + zCount);
        float vectexs[] = new float[(mStrength + 1) * (mStrength + 1) * 3];
        float uvs[] = new float[(mStrength + 1) * (mStrength + 1) * 2];
        float uvs2[] = new float[(mStrength + 1) * (mStrength + 1) * 2];
        int copyStartOffet = startOffset;
        int copyStartEnd = startEnd;
        int j = 0;
        for(int z = 0;z <= zCount;z++){
            //位置数据(每3个构成一个位置属性)
//            GLLog.loge("当前startOffset:" + copyStartOffet + ";总长度:" + terrain.vertexsFloatArray.length);
            for(int i = copyStartOffet * 3;i < copyStartEnd * 3;i++,j++){
//                GLLog.loge("zCount = " + z + "j = " + j);
                vectexs[j] = terrain.vertexsFloatArray[i];
            }
            copyStartOffet += offset;
            copyStartEnd = (int) ((copyStartOffet + (parent.p2.x - parent.p3.x)) + 1);
        }
        //纹理
        copyStartOffet = startOffset;
        copyStartEnd = startEnd;
        j = 0;
        for(int z = 0;z <= zCount;z++){
            for(int i = copyStartOffet * 2;i < copyStartEnd * 2;i++,j++){
                uvs[j] = terrain.uvs[i];
                if(terrain.uvs2 != null){
                    uvs2[j] = terrain.uvs2[i];
                }
            }
            copyStartOffet += offset;
            copyStartEnd = (int) ((copyStartOffet + (parent.p2.x - parent.p3.x)) + 1);
        }
        //索引
        //索引数据(每个网格6个索引)
        int indexs[] = new int[mStrength * mStrength * 6];
        parent.mIndexCount = indexs.length;
        for(int i = 0,line = 0,rect = 0;i < indexs.length;line += 1,rect+=1){
            //每个网格6个索引
            if(rect != 0 && rect % mStrength == 0){
                line+=1;
            }
            indexs[i] = line + mStrength + 2;		//当前网格下一行的第二个
//            indexs[i] = line + 0;				//当前网格第一行的第一个
            indexs[i + 1] = line + mStrength + 1;		//当前网格下一行的第一个
//            indexs[i + 2] = line + mTerrainWidth + 2;		//当前网格下一行的第二个
            indexs[i + 2] = line + 0;				//当前网格第一行的第一个
            indexs[i + 3] = line + 0;			//当前网格第一行的第一个
//            indexs[i + 3] = line + mTerrainWidth + 2;		//当前网格下一行的第二个
            indexs[i + 4] = line + 1;			//当前网格第一行的第二个
//            indexs[i + 5] = line + 0;			//当前网格第一行的第一个
            indexs[i + 5] = line + mStrength + 2;		//当前网格下一行的第二个
            i += 6;
        }
        //根据索引生成mStrength*mStrength个三角网格,每个三角网格需要存储一个法线向量,以便用于平面方程(保存到这个节点中,以便在查找某点的高度值时使用)
        markTerrainTriangles(parent, vectexs, indexs);
        //生成对应vao
        int []vao = new int[1];
        int []vbo = new int[1];
        int []ebo = new int[1];
        FloatBuffer fb, uvFB, uvFB2 = null;
        IntBuffer eboFB;
        fb = GLBufferTools.getFloatBuffer(vectexs);
        uvFB = GLBufferTools.getFloatBuffer(uvs);
        if(uvs2 != null){
            uvFB2 = GLBufferTools.getFloatBuffer(uvs2);
        }
        eboFB = GLBufferTools.getIntBuffer(indexs);
        GLES32.glGenVertexArrays(1, vao, 0);
        GLES32.glGenBuffers(1, vbo, 0);
        GLES32.glGenBuffers(1, ebo, 0);
        GLES32.glBindVertexArray(vao[0]);
        GLES32.glBindBuffer(GLES32.GL_ARRAY_BUFFER, vbo[0]);
        if(uvFB2 != null){
            GLES32.glBufferData(GLES32.GL_ARRAY_BUFFER, (fb.limit() + uvFB.limit() + uvFB2.limit()) * 4, null, GLES32.GL_STATIC_DRAW);
            GLES32.glBufferSubData(GLES32.GL_ARRAY_BUFFER, (fb.limit() + uvFB.limit()) * 4, uvFB2.limit() * 4, uvFB2);
            GLES32.glVertexAttribPointer(0x003, 2, GLES32.GL_FLOAT, false, SizeOf.S_FLOAT_SIZE * 2, (fb.limit() + uvFB.limit()) * 4);
            GLES32.glEnableVertexAttribArray(0x003);
        }
        else{
            GLES32.glBufferData(GLES32.GL_ARRAY_BUFFER, (fb.limit() + uvFB.limit()) * 4, null, GLES32.GL_STATIC_DRAW);
        }
        GLES32.glBufferSubData(GLES32.GL_ARRAY_BUFFER, 0, fb.limit() * 4, fb);
        GLES32.glBufferSubData(GLES32.GL_ARRAY_BUFFER, fb.limit() * 4, uvFB.limit() * 4, uvFB);
        GLES32.glVertexAttribPointer(0x001, 3, GLES32.GL_FLOAT, false, SizeOf.S_FLOAT_SIZE * 3, 0);
        GLES32.glEnableVertexAttribArray(0x001);
        GLES32.glVertexAttribPointer(0x002, 2, GLES32.GL_FLOAT, false, SizeOf.S_FLOAT_SIZE * 2, (fb.limit()) * 4);
        GLES32.glEnableVertexAttribArray(0x002);
        GLES32.glBindBuffer(GLES32.GL_ARRAY_BUFFER, 0);
        GLES32.glBindBuffer(GLES32.GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
        GLES32.glBufferData(GLES32.GL_ELEMENT_ARRAY_BUFFER, eboFB.limit() * 4, eboFB, GLES32.GL_STATIC_DRAW);
        GLES32.glBindVertexArray(0);
        GLES32.glBindBuffer(GLES32.GL_ELEMENT_ARRAY_BUFFER, 0);
        parent.mVAO = vao[0];
        parent.mIndexCount = indexs.length;
        for(int error;(error = GLES32.glGetError()) != GLES32.GL_NO_ERROR;){
            GLLog.loge("error : " + error);
        }
//        GLLog.loge("terrain.vertexsFloatArray : " + Arrays.toString(terrain.vertexsFloatArray));
//        GLLog.loge("构建的顶点数组:" + Arrays.toString(vectexs));
//        GLLog.loge("索引数组:" + Arrays.toString(indexs));ring(terrain.vertexsFloatArray));
//        GLLog.loge("构建的顶点数组:" + Arrays.toString(vectexs));
//        GLLog.loge("索引数组:" + Arrays.toString(indexs));
    }

    /**
     * 为节点生成mStrength*mStrength个三角网格,用于查找某点高度值
     * @param quadTree
     * @param vectexs
     * @param indexs
     */
    private final void markTerrainTriangles(QuadTree quadTree, float[] vectexs, int[] indexs){
        quadTree.mTerrainTriangles = new ArrayList<>();
        for(int i = 0;i < indexs.length;i+=3){
            Vector3f p0 = new Vector3f(vectexs[indexs[i] * 3], vectexs[indexs[i] * 3 + 1], vectexs[indexs[i] * 3 + 2]);
            Vector3f p1 = new Vector3f(vectexs[indexs[i + 1] * 3], vectexs[indexs[i + 1] * 3 + 1], vectexs[indexs[i + 1] * 3 + 2]);
            Vector3f p2 = new Vector3f(vectexs[indexs[i + 2] * 3], vectexs[indexs[i + 2] * 3 + 1], vectexs[indexs[i + 2] * 3 + 2]);
            quadTree.mTerrainTriangles.add(new QuadTree.TerrainTriangle(p0, p1, p2));
        }
    }
    public final float lookAtTriangle(float x, float z){
        return getTriangle(mRoot, x, z);
    }
    public final float getTriangle(QuadTree quadTree, float x, float z){
        if(quadTree.mDepth != 0){
            for(int i = 0;i < quadTree.mChild.size();i++){
                if(quadTree.mChild.get(i).contains(x, z)){
                    return getTriangle(quadTree.mChild.get(i), x, z);
                }
            }
            return -1;
        }
        else{
            //找出具体的三角形
            for(int i = 0;i < quadTree.mTerrainTriangles.size();i++){
                if(quadTree.mTerrainTriangles.get(i).contains2(x, z)){
                    QuadTree.TerrainTriangle terrainTriangle = quadTree.mTerrainTriangles.get(i);
                    //n(A,B,C)
                    //A(x-x0)+B(y-y0)+C(z-z0)=0
                    float A = terrainTriangle.normal.x;
                    float B = terrainTriangle.normal.y;
                    float C = terrainTriangle.normal.z;
                    float y = (A * (x - terrainTriangle.p0.x) + C * (z - terrainTriangle.p0.z)) / B + terrainTriangle.p0.y;
                    //GLGraphics.getCamera().eye.vec[1] = y + 5.0f;
                    return y;
                }
            }
            return -1;
        }
    }
    public final void clear(){
        mRenderList.clear();
    }
    /**
     * 找出(x,z)所在区域的8*8网格,将这些加入到渲染列表中<br/>
     * 我们使用简化的查找算法,每次都找出depth=1的节点加入渲染列表即可,<br/>
     * 而不是用精确查找算法,虽然会造成渲染浪费,每次渲染8*8*3*2个顶点,<br/>
     * 这在cpu端可以加速查找,但是会给gpu端造成渲染浪费.<br/>
     * @param x
     * @param z
     */
    public final void update(float x, float z){
        //计算p*v矩阵
        //Matrix.multiplyMM();
        mRenderList.clear();
        //开始查找
        //构建三个边数据,确保每个line:x>x2,z>z2
        //lookAt(mRoot, markLine(x, z, 20, 10), markLine(x, z, -20, 10), markLine(20, 10, -20, 10));
        lookAt(mRoot, x, z, GLGraphics.getCamera().at2f, 0, 150.0f);
    }
    private final float[] markLine(float x, float z, float x2, float z2){
        float[] line = new float[4];
        if(x > x2){
            line[0] = x;
            line[2] = x2;
        }
        else{
            line[0] = x2;
            line[2] = x;
        }
        if(z > z2){
            line[1] = z;
            line[3] = z2;
        }
        else{
            line[1] = z2;
            line[3] = z;
        }
        return line;
    }
    public final void lookAt(QuadTree quadTree, float x, float z, Vector2fv at, double viewDot, float far){
        if(quadTree.mDepth != 0){
            //查找
            for(int i = 0;i < quadTree.mChild.size();i++){
                //1.检查eye点是否在当前节点管辖区域中
                //2.检查管辖区域4个顶点与朝向向量点乘,结果值应该小于等于视锥体一半
                //3.检查管辖区域4个顶点与eye点的距离是否超过指定长度
//                if(quadTree.mChild.get(i).checkTriangles(line1, line2, line3)){
//                    lookAt(quadTree.mChild.get(i), line1, line2, line3);
//                }
                if(quadTree.mChild.get(i).checkViewRect(x, z, at, viewDot, far)){
                    lookAt(quadTree.mChild.get(i), x, z, at, viewDot, far);
                }
            }
        }
        else{
            //将该节点的4个子节点作为渲染列表数据
            mRenderList.add(quadTree);
        }
    }
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public final void render(){
        if(mRenderList == null || mRenderList.size() <= 0){
            return;
        }
//        render(mRoot);
        for(int i = 0;i < mRenderList.size();i++){
            GLES32.glBindVertexArray(mRenderList.get(i).mVAO);
            GLES32.glDrawElements(GLES32.GL_TRIANGLES, mRenderList.get(i).mIndexCount, GLES32.GL_UNSIGNED_INT, 0);
            GLES32.glBindVertexArray(0);
        }
    }
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public final void render(QuadTree quadTree){
        if(quadTree.mDepth == 0){
//            GLLog.loge("当前树深度:" + quadTree.mDepth + ",vao:" + quadTree.mVAO + ",索引数目:" + quadTree.mIndexCount);
            GLES32.glBindVertexArray(quadTree.mVAO);
            GLES32.glDrawElements(GLES32.GL_TRIANGLES, quadTree.mIndexCount, GLES32.GL_UNSIGNED_INT, 0);
            GLES32.glBindVertexArray(0);
        }
        else{
            for(int i = 0;i < quadTree.mChild.size();i++){
//                GLLog.loge("当前树深度:" + quadTree.mDepth + ":子节点数目:" + quadTree.mChild.size());
                render(quadTree.mChild.get(i));
            }
        }
    }
}
